from django.shortcuts import render
from rest_framework.views import APIView
from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework import status
import base64
import pickle

from . import serializers
from goods.models import SKU


# Create your views here.


class CartView(APIView):
    """购物车增/删/改/查"""

    def perform_authentication(self, request):
        """重写用户登录认证方法,dispatch前不进行验证"""
        pass

    def post(self, request):
        """增"""
        # 获取序列化器对象
        serializer = serializers.CartSerializer(data=request.data)
        # 验证
        serializer.is_valid(raise_exception=True)

        # 获取验证通过的数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        # 判断用户是否存在
        try:
            user = request.user
        except Exception:
            user = None

        # 用户存在的情况
        if user is not None and user.is_authenticated:
            # 将购物车数据存储到redis中
            redis_conn = get_redis_connection('cart')
            # 创建redis管道
            pl = redis_conn.pipeline()

            # 使用hash格式进行存储
            pl.hincrby('cart_%s' % user.id, sku_id, count)

            # 判断商品是否勾选并使用set格式存储进redis中
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)

            # 执行管道
            pl.execute()

            # 响应结果,返回数据,状态码:201
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        else:
            # 用户未登录,使用cookie存储购物车商品信息
            cart_str = request.COOKIES.get('cart')
            # 如果购物车有商品
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 如果购物车没有商品,表示用户没有在cookie中存储过商品信息
                cart_dict = {}

            # 判断前端传入的商品信息是否在购物车中
            if sku_id in cart_dict:
                # 取出原始的值进行累加
                origin_count = cart_dict[sku_id]['count']
                count += origin_count

            # 前端传入的商品不在购物车中,将累加或最新的count保存到字典中
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 将字典转成字符串
            cart_dict_bytes1 = pickle.dumps(cart_dict)
            # 使用base64编码
            cart_dict_bytes2 = base64.b64encode(cart_dict_bytes1)
            # 转成字符串
            cart_dict_str = cart_dict_bytes2.decode()

            # 构造响应对象
            response = Response(serializer.data, status=status.HTTP_201_CREATED)

            # 设置响应对象的cookie
            response.set_cookie('cart', cart_dict_str)

            # 响应数据
            return response

    def get(self, request):
        """查"""
        # 判断用户是否登录
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 用户登录
            # 获取用户购物车数据
            redis_conn = get_redis_connection('cart')
            # 获取购物车中的商品信息:返回字典,但是bytes类型
            redis_cart = redis_conn.hgetall('cart_%s' % user.id)
            # 获取购物车商品勾选状态
            sku_selected = redis_conn.smembers('selected_%s' % user.id)

            # 整合商品信息及商品勾选状态信息
            cart_dict = {}

            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in sku_selected
                }
        else:
            # 用户未登录,获取cookie中的购物车信息
            cart_str = request.COOKIES.get('cart')
            # 如果购物车有商品
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 如果购物车没有商品,表示用户没有在cookie中存储过商品信息
                cart_dict = {}

        # 获取购物车中的所有商品id
        sku_ids = cart_dict.keys()
        # 使用范围查询,查询数据库,获取商品列表
        skus = SKU.objects.filter(id__in=sku_ids)

        # 给商品信息增加购物车字段:count和selected
        for sku in skus:
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']

        # 创建序列化器对象进行序列化输出
        serializer = serializers.SKUCartSerializer(skus, many=True)

        # 响应数据,get请求方式默认状态码200
        return Response(serializer.data)

    def put(self, request):
        """改"""
        # 指定序列化器对象
        serializer = serializers.CartSerializer(data=request.data)
        # 进行验证
        serializer.is_valid(raise_exception=True)

        # 获取验证后的数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        # 判断用户是否登录
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 用户登录
            redis_conn = get_redis_connection('cart')
            # 定义管道
            pl = redis_conn.pipeline()
            # 修改购物车中商品数量为前端传入的新值
            pl.hset('cart_%s' % user.id, sku_id, count)
            # 修改购物车勾选信息
            if selected:
                # 如果selected传入是True,则添加到redis中
                pl.sadd('selected_%s' % user.id, sku_id)
            else:
                # 如果selected传入的是Fasle,则从redis中移除
                pl.srem('selected_%s' % user.id, sku_id)

            # 执行管道
            pl.execute()

            # 响应数据 GET/PUT成功状态码默认为200
            return Response(serializer.data)
        else:
            # 用户未登录
            # 用户未登录,使用cookie存储购物车商品信息
            cart_str = request.COOKIES.get('cart')
            # 如果购物车有商品
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 如果购物车没有商品,表示用户没有在cookie中存储过商品信息
                cart_dict = {}
            # 使用幂等的方式直接覆盖写入
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 转换成str格式
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

            # 创建response对象
            response = Response(serializer.data)

            # 设置cookie
            response.set_cookie('cart', cart_str)

            # 返回响应
            return response

    def delete(self, request):
        """删"""
        # 创建序列化器对象
        serializer = serializers.CartDeleteSerializer(data=request.data)

        # 验证
        serializer.is_valid(raise_exception=True)

        # 获取验证后的数据
        sku_id = serializer.validated_data.get('sku_id')

        # 判断用户是否登录
        try:
            user = request.user
        except Exception:
            user = None

        # 用户登录
        if user is not None and user.is_authenticated:
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')

            # 删除redis中购物车指定商品信息
            redis_conn.hdel('cart_%s' % user.id, sku_id)
            # 移除redis中购物车指定商品勾选标记
            redis_conn.srem('selected_%s' % user.id, sku_id)

            # 返回响应,仅响应204状态码
            return Response(status=status.HTTP_204_NO_CONTENT)

        else:
            # 用户未登录,使用cookie存储购物车商品信息
            cart_str = request.COOKIES.get('cart')
            # 如果购物车有商品
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 如果购物车没有商品,表示用户没有在cookie中存储过商品信息
                cart_dict = {}

            # 构造响应对象
            response = Response(status=status.HTTP_204_NO_CONTENT)

            if sku_id in cart_dict:
                del cart_dict[sku_id]

                cart_str = base64.b64encode(pickle.dumps(cart_str)).decode()

                response.set_cookie('cart', cart_str)

            return response


class CartSelectedView(APIView):
    """购物车全选/全不选"""
    def perform_authentication(self, request):
        """重写用户登录认证方法,dispatch前不进行验证"""
        pass

    def put(self, request):
        # 创建序列化器对象
        serializer = serializers.CartSelectedSerializer(data=request.data)

        # 验证
        serializer.is_valid(raise_exception=True)

        # 获取验证结果
        selected = serializer.validated_data.get('selected')

        # 判断用户是否存在
        try:
            user = request.user
        except:
            user = None

        if user is not None and user.is_authenticated:
            # 用户登录
            redis_conn = get_redis_connection('cart')
            # 获取购物车
            cart = redis_conn.hgetall('cart_%s' % user.id)
            # 获取购物车中所有的商品sku_id
            sku_ids = cart.keys()

            if selected:
                # 全选
                redis_conn.sadd('selected_%s' % user.id, *sku_ids)
            else:
                # 全不选
                redis_conn.srem('selected_%s' % user.id, *sku_ids)

            return Response({'message': 'OK'})

        else:
            # 用户未登录
            # 从cookie获取购物车数据
            cart = request.COOKIES.get('cart')

            # 创建响应对应
            response = Response({'message': 'OK'})

            if cart is not None:
                # 购物车不为空
                cart = pickle.loads(base64.b64decode(cart.encode()))
                # 遍历
                for sku_id in cart:
                    cart[sku_id]['selected'] = selected
                cart_cookie = base64.b64encode(pickle.dumps(cart)).decode()

                response.set_cookie('cart', cart_cookie)

            return response
